#include "FlatMemoryAllocator.h"


CFlatMemoryAllocator::CFlatMemoryAllocator( const unsigned long& initial_size )
	: _c_initial_memory_size(initial_size)
{
	allocate_memory();
}

CFlatMemoryAllocator::~CFlatMemoryAllocator(void)
{
	free_current_storage();
}

bool CFlatMemoryAllocator::allocate_memory( const unsigned long& alloc_size )
{
	_old_storage = _current_storage;

	if (0 == alloc_size)
	{
		_current_storage.total_size = (0 == _current_storage.total_size) ? 
										_c_initial_memory_size : 
										(_current_storage.total_size * 2);
	}
	else
	{
		_current_storage.total_size += alloc_size;
	}

	_current_storage.head = new unsigned char[_current_storage.total_size];

	assert(NULL != _current_storage.head);
	if (NULL == _current_storage.head)
	{
		return false;
	}

	_current_storage.tail = _current_storage.head;
	_current_storage.available_size = _current_storage.total_size;

	return true;
}

void CFlatMemoryAllocator::move_old_data()
{
	assert(NULL != _old_storage.head);
	assert(_current_storage.total_size >= _old_storage.total_size);

	if (0 != _old_storage.data_size)
	{
		memcpy(_current_storage.head, _old_storage.head, _old_storage.data_size);
	}

	_current_storage.data_size = _old_storage.data_size;
	_current_storage.tail += _current_storage.data_size;
	_current_storage.available_size = _current_storage.total_size - _current_storage.data_size;

	delete[] _old_storage.head;

	_old_storage.clear();
}

void CFlatMemoryAllocator::free_current_storage()
{
	delete[] _current_storage.head;
	_current_storage.clear();
}

void CFlatMemoryAllocator::free_old_storage()
{
	delete[] _old_storage.head;
	_old_storage.clear();
}

bool CFlatMemoryAllocator::append_data( const void* p_data, const unsigned long& data_size )
{
	assert(NULL != p_data);
	assert(0 != data_size);

	if ((NULL == p_data) || (0 == data_size))
		return false;

	while (_current_storage.available_size < data_size)
	{
		unsigned long alloc_data_size = 0;

		if ((2 * _current_storage.total_size - _current_storage.data_size) < data_size )
		{
			alloc_data_size = data_size;
		}

		if (false == allocate_memory(alloc_data_size))
			return false;

		move_old_data();
	}

	memcpy(_current_storage.tail, p_data, data_size);

	_current_storage.tail += data_size;
	_current_storage.data_size += data_size;
	_current_storage.available_size -= data_size;

	return true;
}

bool CFlatMemoryAllocator::set_data( const void* p_data, const unsigned long& data_size )
{
	assert(NULL != p_data);
	assert(0 != data_size);

	if ((NULL == p_data) || (0 == data_size))
		return false;

	while (_current_storage.available_size < data_size)
	{
		unsigned long alloc_data_size = 0;

		if ((2 * _current_storage.total_size - _current_storage.data_size) < data_size )
		{
			alloc_data_size = data_size;
		}

		if (false == allocate_memory(alloc_data_size))
			return false;
	}

	delete[] _old_storage.head;
	_old_storage.clear();

	memcpy(_current_storage.head, p_data, data_size);

	_current_storage.data_size = data_size;
	_current_storage.tail = _current_storage.head + _current_storage.data_size;
	_current_storage.available_size -= _current_storage.data_size;

	return true;
}

const void* CFlatMemoryAllocator::get_data( unsigned long& data_size )
{
	data_size = _current_storage.data_size;

	return _current_storage.head;
}

void CFlatMemoryAllocator::clear()
{
	free_old_storage();
	free_current_storage();
}
